package com.ikingtech.platform.service.system.variable.controller;

import com.ikingtech.framework.sdk.context.event.TenantDeleteEvent;
import com.ikingtech.framework.sdk.context.event.TenantInitEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.system.variable.VariableEnum;
import com.ikingtech.framework.sdk.enums.system.variable.VariableTypeEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.variable.api.VariableApi;
import com.ikingtech.framework.sdk.variable.model.VariableDTO;
import com.ikingtech.framework.sdk.variable.model.VariableQueryParamDTO;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.variable.entity.VariableDO;
import com.ikingtech.platform.service.system.variable.exception.VariableExceptionInfo;
import com.ikingtech.platform.service.system.variable.service.VariableService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
@ApiController(value = "/system/variable", name = "系统管理-系统参数管理", description = "系统管理-系统参数管理")
public class VariableController implements VariableApi {

    private final VariableService service;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(VariableDTO variable) {
        if (Boolean.TRUE.equals(this.service.nameExist(variable.getName(), Me.tenantCode()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_NAME);
        }
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setTenantCode(Me.tenantCode());
        this.service.save(entity);
        this.service.syncVariableToCache(entity);
        return R.ok(entity.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        this.service.removeById(id);
        return R.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(VariableDTO variable) {
        VariableDO originEntity = this.service.getById(variable.getId());
        if (originEntity == null) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        if (Boolean.TRUE.equals(this.service.nameExist(variable.getId(), variable.getName(), Me.tenantCode()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_NAME);
        }
        if (VariableTypeEnum.SYSTEM.name().equals(originEntity.getType()) &&
                !VariableEnum.validate(variable.getVariableKey(), variable.getVariableValue())) {
                throw new FrameworkException(VariableExceptionInfo.INVALID_VARIABLE_VALUE);

        }
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        entity.setTenantCode(Me.tenantCode());
        this.service.updateById(entity);
        this.service.syncVariableToCache(entity);
        return R.ok();
    }

    @Override
    public R<List<VariableDTO>> page(VariableQueryParamDTO queryParam) {
        return R.ok(this.service.listPage(queryParam, Me.tenantCode()).convert(entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            variable.setTypeName(VariableTypeEnum.valueOf(entity.getType()).description);
            return variable;
        }));
    }

    @Override
    public R<List<VariableDTO>> all() {
        return R.ok(Tools.Coll.convertList(this.service.listByTenantCode(Me.tenantCode()), entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            variable.setTypeName(VariableTypeEnum.valueOf(entity.getType()).description);
            return variable;
        }));
    }

    @Override
    public R<VariableDTO> detail(String id) {
        VariableDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
        variable.setTypeName(VariableTypeEnum.valueOf(entity.getType()).description);
        return R.ok(variable);
    }

    @Override
    public R<String> valueByKey(String key) {
        return R.ok(this.service.getValueByKey(key));
    }

    private VariableDO entityConvert(String tenantCode, VariableEnum variableEnum) {
        VariableDO variableDO = new VariableDO();
        variableDO.setId(Tools.Id.uuid());
        variableDO.setTenantCode(tenantCode);
        variableDO.setVariableKey(variableEnum.name());
        variableDO.setVariableValue(variableEnum.value);
        variableDO.setType(variableEnum.type.name());
        variableDO.setName(variableEnum.description);
        return variableDO;
    }

    @EventListener
    public void tenantInitEventListener(TenantInitEvent event) {
        this.service.saveBatch(Tools.Array.convertList(VariableEnum.values(), variable -> entityConvert(event.getCode(), variable)));
    }

    @EventListener
    public void tenantDeleteEventListener(TenantDeleteEvent event) {
        this.service.removeByTenantCode(event.getCode());
    }

    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedDelay = 5 * 60, initialDelay = 60, timeUnit = TimeUnit.SECONDS)
    public void syncVariableCache() {
        List<VariableDO> entities = this.service.list();
        this.service.syncVariableToCache(entities);
    }
}
